package com.ymx.darling.util;

import com.ymx.darling.bean.annotation.EnumValue;
import com.ymx.darling.exception.EntityException;
import com.ymx.darling.exception.ExceptionFactory;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 枚举处理的工具类
 * @author 爱Java的小于
 */
public class EnumUtil {
    /**
     * 存储Enum值的Field
     */
    private static final Map<Enum<?>, Object> enumValueFieldMap = new ConcurrentHashMap<>();
    /**
     * 根据值存储Enum值
     */
    private static final Map<Class<? extends Enum<?>>, Map<Object, Enum<?>>> enumValueNameMap = new ConcurrentHashMap<>();

    /**
     * 返回枚举常量的值
     * @param enumValue enum
     * @return object
     */
    public static Object getValue(Enum<?> enumValue) {
        return enumValueFieldMap.computeIfAbsent(enumValue, type -> {
            // 检查字段是否存在EnumValue注解
            Class<?> enumValueClass = enumValue.getClass();
            Field[] fields = enumValueClass.getDeclaredFields();
            Field defaultValueField = null;
            for (Field field : fields) {
                if (field.isAnnotationPresent(EnumValue.class)) {
                    if (Objects.nonNull(defaultValueField)) {
                        EntityException exception = new EntityException("枚举" + enumValueClass.getName() + "中不能出现多个@EnumValue的定义");
                        throw ExceptionFactory.wrapException("解析枚举时抛出异常", exception);
                    } else {
                        defaultValueField = field;
                    }
                }
            }
            // 判断存在@EnumValue标注的字段 将字段值作为默认值
            if (Objects.nonNull(defaultValueField)) {
                defaultValueField.setAccessible(true);
                return ReflectUtil.getValue(defaultValueField, enumValue);
                // 否则按照name方法的值作为默认值
            } else {
                return enumValue.name();
            }
        });
    }


    /**
     * 根据enum中的值构建enum实例
     * @param enumType enum类型
     * @param value enum值 如果表示值的字段被@EnumValue注解 那么则使用该字段的值 否则使用name的值
     * @return enum
     */
    public static Enum<?> valueOf(Class<? extends Enum<?>> enumType, Object value) {
        Map<Object, Enum<?>> objectEnumMap = enumValueNameMap.computeIfAbsent(enumType, type -> {
            Map<Object, Enum<?>> enumMap = new HashMap<>();
            for (Enum<?> eEnum : enumType.getEnumConstants()) {
                enumMap.put(getValue(eEnum), eEnum);
            }
            return enumMap;
        });
        return objectEnumMap.get(value);
    }
}
